package cn.pli.femedia.MPEG;

import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.util.Log;

import java.nio.ByteBuffer;

import cn.pli.femedia.MainActivity;
import cn.pli.femedia.MoviePlayer;
import cn.pli.femedia.VideoEncoderCore;

/**
 * Created by foxy on 2017/5/25.
 * 向文件中写入音频数据的线程
 */
public class WriteVideoTask implements Runnable {
    private static final String TAG = "MainActivity.TAG";
    private static final boolean VERBOSE = true;
    // static volatile int inputChunk = 0;
    static volatile int outputChunk = 0;


    @Override
    public void run() {
    }

    /**
     * 主要处理视频解码工作，同时与需要与编码器保持同步，否则容易造成内存溢出
     *
     * @param extractor
     * @param trackIndex
     * @param decoder
     * @param frameCallback
     */
    private void doExtract(MediaExtractor extractor, int trackIndex, MediaCodec decoder, MoviePlayer.FrameCallback frameCallback, MediaCodec.BufferInfo mBufferInfo) {

        final int TIMEOUT_USEC = 10000;
        ByteBuffer[] decoderInputBuffers = decoder.getInputBuffers();
        long firstInputTimeNsec = -1;
        boolean outputDone = false;
        boolean inputDone = false;
        while (!outputDone) {

            if ((outputChunk - VideoEncoderCore.outputNumber) > 4) {
                try {
                    Thread.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                extractor.selectTrack(trackIndex);
                if (!inputDone) {
                    int inputBufIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC);
                    if (inputBufIndex >= 0) {
                        if (firstInputTimeNsec == -1) {
                            firstInputTimeNsec = System.nanoTime();
                        }
                        ByteBuffer inputBuf = decoderInputBuffers[inputBufIndex];
                        // Read the sample data into the ByteBuffer.  This neither respects nor
                        // updates inputBuf's position, limit, etc.
                        int chunkSize = extractor.readSampleData(inputBuf, 0);
                        if (chunkSize < 0) {
                            decoder.queueInputBuffer(inputBufIndex, 0, 0, 0L, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                            inputDone = true;
                        } else {
                            if (extractor.getSampleTrackIndex() != trackIndex) {
                                Log.w(TAG, "WEIRD: got sample from track " + extractor.getSampleTrackIndex() + ", expected " + trackIndex);
                            }
                            long presentationTimeUs = extractor.getSampleTime();
                            decoder.queueInputBuffer(inputBufIndex, 0, chunkSize, presentationTimeUs, 0 /*flags*/);
                            extractor.advance();
                        }
                    } else {
                        if (VERBOSE) Log.d(TAG, "input buffer not available");
                    }
                }
                if (!outputDone) {
                    int decoderStatus = decoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);
                    if (decoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
                        if (VERBOSE){
                            Log.d(TAG, "no output from decoder available");
                        }
                    } else if (decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                        if (VERBOSE) {
                            Log.d(TAG, "decoder output buffers changed");
                        }
                    } else if (decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                        MediaFormat newFormat = decoder.getOutputFormat();
                        if (VERBOSE) {
                            Log.d(TAG, "decoder output format changed: " + newFormat);
                        }
                    } else if (decoderStatus < 0) {
                        throw new RuntimeException("unexpected result from decoder.dequeueOutputBuffer: " + decoderStatus);
                    } else {
                        if (firstInputTimeNsec != 0) {
                            // Log the delay from the first buffer of input to the first buffer
                            long nowNsec = System.nanoTime();
                            Log.d(TAG, "startup lag " + ((nowNsec - firstInputTimeNsec) / 1000000.0) + " ms");
                            firstInputTimeNsec = 0;
                        }
                        if (VERBOSE) {
                            Log.d(TAG, "surface decoder given buffer " + decoderStatus + " (size=" + mBufferInfo.size + ")");
                        }
                        if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                            if (VERBOSE) {
                                Log.d(TAG, "output EOS");
                            }
                            outputDone = true;
                            MainActivity.mHandler.sendEmptyMessage(MainActivity.STOP_RECOEDING);
                        }

                        boolean doRender = (mBufferInfo.size != 0);

                        // As soon as we call releaseOutputBuffer, the buffer will be forwarded
                        // to SurfaceTexture to convert to a texture.  We can't control when it
                        // appears on-screen, but we can manage the pace at which we release
                        // the buffers.
                        if (doRender && frameCallback != null) {
                            frameCallback.preRender(mBufferInfo.presentationTimeUs);

                        }
                        if (MainActivity.isEncoder) {
                            MainActivity.mHandler.sendEmptyMessage(MainActivity.ON_DRAW_FRAME);
                        }
                        if (VERBOSE) {
                            Log.d(TAG, "decoder frame " + outputChunk + " to dec, size="
                            );
                        }
                        outputChunk++;
                        decoder.releaseOutputBuffer(decoderStatus, doRender);
                        if (doRender && frameCallback != null) {
                            frameCallback.postRender();
                        }

                    }
                }
            }
        }


    }


}
